package pipeline.vertex;

import javax.vecmath.Color3f;
import javax.vecmath.Vector2f;
import javax.vecmath.Vector3f;
import javax.vecmath.Vector4f;

import pipeline.Pipeline;
import pipeline.PointLight;
import pipeline.math.Matrix4f;
import pipeline.misc.Vertex;

/**
 * This triangle processor smoothly interpolates the color across the face of
 * the triangle. This is better than flat shading, but still not as nice as
 * fragment (aka phong) shading.
 * 
 * @author ags
 */
public class SmoothShadedVP extends ShadedVP
{
	protected Matrix4f m = new Matrix4f();
	protected Matrix4f modelview = new Matrix4f();

	// Vectors needed to calculate specular and diffuse lighting contributions
	Vector3f E = new Vector3f(), L = new Vector3f(), N = new Vector3f(), H = new Vector3f(), V = new Vector3f();
	
	// Position of vertex and normal in eye coordinates
	Vector4f v2 = new Vector4f(), n2 = new Vector4f();
	
	// Final color of the fragment
	Color3f c2 = new Color3f();
	
	
	public int nAttr()
	{
		return 3;
	}

	public void updateTransforms(Pipeline pipe)
	{
		modelview.set(pipe.modelviewMatrix);
		
		m.set(pipe.modelviewMatrix);
		m.leftCompose(pipe.projectionMatrix);
		m.leftCompose(pipe.viewportMatrix);
	}

	public void vertex(Vector3f v, Color3f c, Vector3f n, Vector2f t_ignore, Vertex output)
	{
		// Position of vextex in eye coordinates
		v2.set(v.x, v.y, v.z, 1);
		modelview.rightMultiply(v2);
		V.set(v2.x, v2.y, v2.z);

		// Normal in eye coordinates
		n2.set(n.x, n.y, n.z, 0);
		modelview.rightMultiply(n2);
		
		E.set(-V.x, -V.y, -V.z);
		E.normalize();
		
		N.set(n2.x, n2.y, n2.z);
		N.normalize();
		
		// Color generated by contribution of all lights
		// Ambient contribution
		c2.set(Pipeline.ambientIntensity*c.x, Pipeline.ambientIntensity*c.y, Pipeline.ambientIntensity*c.z);
		
		// For each light, get the contribution to this vertex
		for(PointLight light : Pipeline.lights) {
			Color3f lc = light.getIntensity();

			// Vector from vertex to light source -- normalized
			L.set(light.getPosition());
			L.sub(V);
			L.normalize();
			
			float dot = L.dot(N);

			// Diffuse contribution
			if(dot > 0) {
				c2.x += c.x*dot*lc.x;
				c2.y += c.y*dot*lc.y;
				c2.z += c.z*dot*lc.z;
			}
			
			// Half vector -- bisector of L and E
			H.set(L);
			H.add(E);
			H.normalize();
			
			dot = H.dot(N);
			
			// Specular contribution
			if(dot > 0) {
				dot = (float) Math.pow(dot, Pipeline.specularExponent);
				
				c2.x += dot*Pipeline.specularColor.x;
				c2.y += dot*Pipeline.specularColor.y;
				c2.z += dot*Pipeline.specularColor.z;
			}
		}
		
		// Clamp rgb to [0,1]
		if(c2.x > 1) {
			c2.x = 1;
		}
		if(c2.y > 1) {
			c2.y = 1;
		}
		if(c2.z > 1) {
			c2.z = 1;
		}
		
		output.v.set(v.x, v.y, v.z, 1);
		m.rightMultiply(output.v);
		
		output.setAttrs(nAttr());
		output.attrs[0] = c2.x;
		output.attrs[1] = c2.y;
		output.attrs[2] = c2.z;
	}
	
	public void triangle(Vector3f[] v, Color3f[] c, Vector3f[] n, Vector2f[] t, Vertex[] output)
	{
		for (int k = 0; k < 3; k++) {
			vertex(v[k], c[k], n[k], t[k], output[k]);
		}
	}
}
